후처리 전략

후처리란?

ChatGPT, Claude와 같은 서비스의 경우 모델을 외부에 공개하지 않습니다. 이런 LLM을 사용할때 새로운 모델이 나오면 같은 프롬프트를 사용하더라도 결과가 바뀌게 되는 경우가 생깊니다.

후처리는 이런 상황에서 LLM의 결과물의 일관성과 안전성을 확보하는 방법입니다.

Self-Consistency

LLM에게 질문하는 상황에서 LLM들은 같은 질문을 하더라도 매번 다르게 대답합니다.

만약 같은 질문에 대해서 같은 답 출력하게 만들고 싶다면 온도(Temperature)이라는 값을 조절해서 질문에 대한 LLM의 답변을 일관성 있게 만들수 있습니다. LLM 및 딥러닝에서는 온도를 통해서 응답의 무작위성을 조정할 수 있습니다. 0에 가까울수록 고정된 값이 나오며 1에 가까울 수록 더 무작위한 값이 나오게 됩니다.

하지만 일관성 있는 대답을 위해서 Temperature을 0으로 설정하게 된다면, 각종 버그와 단점이 따라올수 있습니다. 모델에 따라서 온도가 강제되는 사례또한 있었으며 0인데도 불과하고 답변이 무작위로 나오는 버그 또한 있습니다. 최종적으로 온도를 0으로 할경우 가장 확률이 높은 대답을 주기 때문에 오답을 말하는 경우가 막기 힘들어 집니다.

따라서 Self-consistency(자기 일관성)은 이런 Temperature가 0일 경우 생기는 버그를 막으며 일관성 있는 대답을 주기 위한 열구중 1개였습니다.

self-consistency의 동작원리

self-consistency는 하나의 문제에 대래서 여러번의 질문을 통해 여러개의 답변을 생성합니다. 이중 최빈값을 확인하여 올바른 답을 얻을 가능성을 높입니다.

만약 수학문제와 같은 최빈값을 정량적으로 측정하기 힘든 상황의 경우, 모든 답변을 종합해서 올바른 정답을 유도하는 후속처리 모델 및 프롬프트를 두어 이를 해결합니다.

이런 self-consistency와 관련된 연구는 MedPrompt가 있습니다.

Structured Outputs

LLM을 사용하는 서비스에서 문제중 하나는 자연어 출력물을 정제하기 어렵다는 것입니다. 실제로 나올 수 있는 답변은 매우 다양하며, 이런 다양한 답변을 특정 규칙에 맞게 데이터를 정제한다는 것은 매우 어려운 일입니다.

만약 다음과 같은 질문을 한다고 해보겠습니다.

질문 > A 농장에서는 매일 우유 3병과 계란 20개가 나옵니다. 이 중 우유 1병과 계란 2개를 밥을 먹기 위해서 사용하며, 추가로 계란 3개를 매일 친구에게 나눠줍니다. 남은 우유를 팔면 20$를 벌수 있으며, 남은 계란을 팔경우 30$를 벌수 있습니다. 그럼 그녀는 하루에 우유 1병과 계란 1개를 얼마 팔고 있는 것인까료?
 
대답 1 > 우유 : 10$, 계란 : 2$
대답 2 > 우유 1병에 10$, 계란 1개에 2$
대답 3 > 우유 1병 십 달러, 계란 1개 이 달러

이렇게 다양한 대답은 Regex(정규식)을 통해서 따로 추출이 가능하나, 실제 이에 따른 강제되는 경우의 수가 너무 많습니다.

이를 해결하기 위해서 JSON, XML과 같은 문서 형태를 이용하서 강제하는 합성 방법론이 Structured output입니다.

{
  "steps" : [
    {
      "explanation" : "매일 우유 3병이 나오고 밥을 먹는 데 1병을 사용하므로 판매 가능한 우유는 3 - 1 = 2병입니다.",
      "output" : "남은 우유 = 2병"
    },
    {
      "explanation" : "남은 우유 2병을 팔아 20$를 벌므로 우유 1병의 가격은 20$ / 2 = 10$입니다.",
      "output" : "우유 1병 = 10$"
    },
    {
      "explanation" : "매일 계란 20개가 나오고 밥에 2개, 친구에게 3개를 주므로 판매 가능한 계란은 20 - 2 - 3 = 15개입니다.",
      "output" : "남은 계란 = 15개"
    },
    {
      "explanation" : "남은 계란 15개를 팔아 30$를 벌므로 계란 1개의 가격은 30$ / 15 = 2$입니다.",
      "output" : "계란 1개 = 2$"
    }
  ],
  "final_answer" : "우유 1병 = 10$, 계란 1개 = 2$"
}

하지만 이런 Structured output 기능을 사용하더라도 출력 생성에 실패하는 경우 혹은 서로 다른 팀에서 LLM을 사용하면서 관리 해야하는 경우 등 다양한 상황에서 직면할 수 있습니다. 이때 사용하기 좋은 라이브러리가 Instructor입니다.

from pydantic import BaseModel
import instructor
 
client = instructor.from_provider("openai/gpt-4o-mini")
 
class Product(BaseModel):
  name: str
  price: float
  in_stock: bool
 
product = client.chat.completions.create(
  response_model=Product,
  massages[{"role":"user", "content":"iPhone 15 Pro, $999, available now"}],
)
 
print(product)

만약 Opensource LLM을 직접 사용할 경우 Outlines와 같은 Context-free grammer 기반의 디코딩 알고리즘을 사용하여 파이썬 패키지를 사용해볼 수 있습니다.

import outlines
from transformers import AutoTokenizer, AutoModelForCausalLM
from typing import Literal
from pydantic import BaseModel
 
MODEL_NAME = "microsoft/PHi-3-mini-4k-instruct"
model = outlines.from_transformers(
  AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto"),
  AutoTokenizer.from_pretrained(MODEL_NAME)
)
 
sentiment = model(
  "Analyze: 'This product completely changed my life!'",
  Literal["Positive", "Negative", "Neutral"]
)
 
print(sentiment)
 
temperature = model("What's the boiling point of water in Celsius?", int)
print(temperature)

Structued Output 문제점

Structured Output은 출력을 강제할 수 있지만, 작업에 따라서 성능이 떨어질수 있다는 연구 결과가 있습니다.